﻿using Microsoft.Xna.Framework;

namespace SaveTheQueen.Actions
{
    /// <summary>
    /// Delegate used to be called when actions are  updated.
    /// </summary>
    public delegate void UpdateValues();
    /// <summary>
    /// Delegate used to be called when actions finish their work.
    /// </summary>
    public delegate void FinishedAction();

    /// <summary>
    /// Base action to be implemented by the implemented actions.
    /// <remarks>Does most of the work as keep track of time and trigger the events.</remarks>
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class BaseAction<T> : CoreAction
    {
        private T input;
        private T goal;
        private T actual;

        /// <summary>
        /// Time used to complete the action
        /// </summary>
        protected float time;
        /// <summary>
        /// Time elapsed since the action start.
        /// </summary>
        protected float elapsedTime;

        /// <summary>
        /// Creates a new Instance of the BaseAction.
        /// </summary>
        /// <param name="game"></param>
        public BaseAction(Game game) : base(game) { }
        /// <summary>
        /// Start the action work.
        /// </summary>
        /// <param name="input">Starting value of the action</param>
        /// <param name="goal">Ending value of the action</param>
        /// <param name="time">Time taken to complete the action</param>
        public void Start(T input, T goal, float time)
        {
            this.input = input;
            this.goal = goal;
            this.time = time;

            this.isFinished = false;
            this.isRunning = true;
            this.actual = input;
            this.elapsedTime = 0;
        }

        public override void Update(GameTime gameTime)
        {
            if (isRunning)
            {
                elapsedTime += (float)gameTime.ElapsedGameTime.TotalSeconds;
                if (elapsedTime > time)
                {
                    if (isLopping)
                    {   // it call the finish, but it continue to act
                        elapsedTime = 0;
                        OnFinished();
                    }
                    else
                    {
                        isRunning = false;
                        isFinished = true;
                        OnFinished();
                    }
                }
                else
                {
                    Process(gameTime);
                    OnUpdate();
                }
            }
            base.Update(gameTime);
        }

        /// <summary>
        /// Event raised when the action is updated.
        /// <remarks>Use it to set the generated values of the action elsewhere</remarks>
        /// </summary>
        public event UpdateValues OnUpdate;
        public event FinishedAction OnFinished;

        public void ClearEvents()
        {
            OnUpdate = null;
            OnFinished = null;
        }
        public void CreateEvents()
        {
            OnUpdate = delegate() { };
            OnFinished = delegate() { };
        }

        protected abstract void Process(GameTime gameTime);

        public T Input
        {
            get { return input; }
        }
        public T Goal
        {
            get { return goal; }
        }
        public T Actual
        {
            get { return actual; }
            protected set { actual = value; }
        }
    }
}
